package com.ben.maven.test.ocrs.ns.jaxb.sei.v1_0_2;

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;

import javax.net.ssl.SSLHandshakeException;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.NoHttpResponseException;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.utils.URIUtils;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.ExecutionContext;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;


public class OcrsHttpClient<A, B> {
	
	public DefaultHttpClient httpClient = new DefaultHttpClient();
	
	public HttpRequestRetryHandler retryHandler = new HttpRequestRetryHandler() {

		public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
			if(executionCount >= 5) {
				//Do not retry if over max retry count 
				return false;
			}
			if(exception instanceof NoHttpResponseException) {
				//Retry if the server dropped connection on us
				return true;
			}
			if(exception instanceof SSLHandshakeException) {
				//Do not retry on SSL handshake exception
				return false;
			}
			HttpRequest request = (HttpRequest)context.getAttribute(ExecutionContext.HTTP_REQUEST);
			boolean idempotent = !(request instanceof HttpEntityEnclosingRequest);
			if(idempotent) {
				//Retry if the request is considered idempotent
				return true;
			}
			return false;
		}
		
	};
	
	public String requestService(Class<A> requestClass, A request) throws Exception {
		
		JAXBContext searchRQjc = JAXBContext.newInstance(requestClass);
		Marshaller marshaller = searchRQjc.createMarshaller();
		
		StringWriter xmlStringWriter = new StringWriter();
		marshaller.marshal(request, xmlStringWriter);

		System.out.println("Xml String Stream : " + xmlStringWriter);
		
		String xmlString = xmlStringWriter.toString();
		System.out.println("Xml String : " + xmlString);
		return xmlString;
	}
	
	public B responseService(Class<B> responseClass, StringReader xmlStringReader) throws Exception {
		//在此之前不能读取xmlStringReader中的内容，否则JAXB在解析xml时会抛出SAX : Premature end of file异常。
		//这里要使用SearchRS.class创建另一个JAXBContext实例，因为之前的JAXBContext实例是SearchRQ.class的，所以xml文件是以SearchRQ为根节点的，这里的Response是要以SearchRS为根节点的xml
		//因为我们所有的Request和Response都用的一个dtd文件定义的，所以xml的根节点可以是SearchRQ也可以是SearchRS，要根据实际的Request和Response而定。
		//因为所有xjc工具生成的实体类都有@XmlRootElement注解，所以都可以为根节点，所以这里要使用不同的JAXBContext来定义对应转换的实体类。
		//JAXBContext searchRSjc = JAXBContext.newInstance(SearchRS.class);
		JAXBContext searchRSjc = JAXBContext.newInstance(responseClass);
		Unmarshaller unmarshaller = searchRSjc.createUnmarshaller();
		B response = (B)unmarshaller.unmarshal(xmlStringReader);
		return response;
	}
	
	public B callService(Class<A> requestClass, Class<B> responseClass, A request) throws Exception {
		
		httpClient.setHttpRequestRetryHandler(retryHandler);
		
		String xmlString = requestService(requestClass, request);
		
		List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>();
		params.add(new BasicNameValuePair("xml", xmlString));
		String param = URLEncodedUtils.format(params, "UTF-8");
		URI uri = URIUtils.createURI("http", "www.rentalcars.com", -1, "/service/ServiceRequest.do", param, null);
		
		HttpUriRequest getRequest = new HttpGet(uri);
		System.out.println("Request uri : " + getRequest.getURI());
		
		HttpResponse httpResponse = httpClient.execute(getRequest);
		HttpEntity httpEntity = httpResponse.getEntity();
		//System.out.println("Response content string : " + new String(EntityUtils.toByteArray(httpEntity)));
		//这里因为Response中<Description>元素中存在中文，所以这里一定要使用UTF-8进行转码，否则JAXB在解析xml时会抛出异常无法找到</Description>元素，因为中文解析成乱码，导致</Description>元素无法识别。
		StringReader xmlStringReader = new StringReader(new String(EntityUtils.toByteArray(httpEntity), "UTF-8"));
		
		/*File file = new File("src/test/java/com/ben/maven/test/jaxb/dtd2java/httpclient/newSearchRS.xml");
		file.createNewFile();
		OutputStream outputStream = new FileOutputStream(file);
		int c;
		while((c = xmlStringReader.read()) != -1) {
			System.out.print((char)c);
			outputStream.write(c);
		}*/

		B response = responseService(responseClass, xmlStringReader);
		
		return response;
	}

}
